knitr::opts_chunk$set(warning = FALSE, error = FALSE)
library(dplyr)
library(tidyr)
library(ggplot2)
library(readr)
library(DescTools)
library(sf)
library(tmap)
library(corrplot)
library(data.table)
library(purrr)
library(car)
library(Metrics)
library(rsq)
library(jtools)
library(gt)
The aim of this analysis is to study the impact of recent migration to Vienna on the election results of the national election in 2024.
The analysis is carried out on a district level, studying the average yearly migration to each district and correlating it with percentage of votes for each party in the districts of Vienna.
The types of migration studied are: * Net external migration (ext_net): net migration to Vienna from abroad * Net internal migration (int_net): net migration to Vienna from Austria * Net local migration (local_net): net migration between Viennese districts * internal local migration (local_internal): total migration within Viennese districts
The source data is given in total numbers of persons migration, which for the sake of comparisons is recalculated as percentage of population living in a district.
For the analysis, a linear regression model is implemented for each party, with the election results for the given party as target variable.
The analysis is complemented with the sociodemographic variables of average age, percentage of females and income for each district for 2023 to control for their influence on voting pattern.
We start by importing the necessary data for the analysis. The election results have been sourced from https://www.wien.gv.at/wahlergebnis/de/NR241/index.html, the yearly migration from 2007 to 2023 per district with population and ge data has been cleaned in previous analysis and is accessed through the output_data folder. The social demographic indicators are sourced from https://www.data.gv.at/katalog/dataset/9ecf5866-dbe8-4cb2-b156-5097c7eec01f
We tidy up the election by removing non relevant columns and rows, renaming columns and turning the number of votes into a percentage of votes for each party.
head(election)
## # A tibble: 6 × 19
## NUTS Gebietsname Wahlberechtigte Abgegebene `Ung?ltige` `G?ltige` `?VP`
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 G00000 ?sterreich 6346059 4929745 46857 4882888 1.28e6
## 2 G10000 Burgenland 233738 192899 2800 190099 5.43e4
## 3 G10099 Wahlkarten - B… 0 41 41 0 0
## 4 G1A000 Burgenland Nord 124968 103415 1389 102026 2.80e4
## 5 G1A099 Wahlkarten - B… 0 303 2 301 4.9 e1
## 6 G1B000 Burgenland S?d 108770 89443 1370 88073 2.63e4
## # ℹ 12 more variables: `SP?` <dbl>, `FP?` <dbl>, `GR?NE` <dbl>, NEOS <dbl>,
## # BIER <dbl>, MFG <dbl>, BGE <dbl>, LMP <dbl>, GAZA <dbl>, `KP?` <dbl>,
## # KEINE <dbl>, ...19 <lgl>
election = election %>% filter(substr(NUTS, 1, 2) == "G9" &
Wahlberechtigte > 0 &
substr(NUTS, 5, 6) == "00")
election = election[9:nrow(election),]
election$bezirk = 1:23
election = subset(
election,
select = -c(
NUTS,
Gebietsname,
Wahlberechtigte,
Abgegebene,
`Ung?ltige`,
`G?ltige`,
...19,
BGE
)
)
colnames(election) = c(
"ÖVP",
"SPÖ",
"FPÖ",
"GRÜNE",
"NEOS",
'BIER',
"MFG",
"LMP",
"GAZA",
"KPÖ",
"KEINE",
"bezirk"
)
election$bezirk = as.factor(election$bezirk)
# turn election results into percentage
perc = function(vec) {
vec = vec / sum(vec)
}
election = apply(election %>% select(!bezirk),
FUN = perc,
MARGIN = 1)
election = t(as.data.frame(election))
election = election * 100
rownames(election) = 1:nrow(election)
election = as.data.frame(election)
election$bezirk = as.factor(1:nrow(election))
parliament = c("SPÖ", "ÖVP", "FPÖ", "GRÜNE", "NEOS")
election = election[,c(parliament, "bezirk")]
The subdistricts are first summed up into district (Bezirk) results and then averaged over time. The dataframe is then split into two, one for austrian migration and one for foreign migration. Columns indicating net migration are kept.
#transforming yearly migration to percentage of current population of that year
year_bezirk_sex_nat = df %>% group_by(year, bezirk, sex, nationality) %>%
summarize(
net = sum(net),
ext_net = sum(ext_net),
int_net = sum(int_net),
local_net = sum(local_net),
local_internal = sum(local_internal),
net_p = sum(net_p),
ext_net_p = sum(ext_net_p),
int_net_p = sum(int_net_p),
local_net_p = sum(local_net_p),
local_internal_p = sum(local_internal_p)
)
## `summarise()` has grouped output by 'year', 'bezirk', 'sex'. You can override
## using the `.groups` argument.
#average yearly net migration from 2007 to 2023 per nationality
bezirk_nat = year_bezirk_sex_nat %>% group_by(bezirk, nationality) %>%
summarize(
net_p = mean(net_p),
ext_net_p = mean(ext_net_p),
int_net_p = mean(int_net_p),
local_net_p = mean(local_net_p),
local_internal_p = mean(local_internal_p)
)
## `summarise()` has grouped output by 'bezirk'. You can override using the
## `.groups` argument.
bezirk_nat$bezirk = as.factor(bezirk_nat$bezirk)
bezirk_election = bezirk_nat %>% inner_join(election, by = c("bezirk" = "bezirk"))
tot_mig = as.data.frame(bezirk_election) %>% group_by(bezirk) %>% summarize(
net_p = sum(net_p),
ext_net_p = sum(ext_net_p),
int_net_p = sum(int_net_p),
local_net_p = sum(local_net_p),
local_internal_p = sum(local_internal_p)
)
tot_mig = as.data.frame(tot_mig) %>%
inner_join(election, by = c("bezirk" = "bezirk"))
aut_mig = as.data.frame(bezirk_election) %>% filter(nationality == "Austrian")
for_mig = as.data.frame(bezirk_election) %>% filter(nationality == "Foreign")
head(tot_mig)
## bezirk net_p ext_net_p int_net_p local_net_p local_internal_p SPÖ
## 1 1 0.7985032 0.6827393 0.1157639 -0.7353247 0.2340946 19.11643
## 2 2 0.8893954 0.6366993 0.2526961 -0.4800800 0.4762326 33.18164
## 3 3 0.7664210 0.6294257 0.1369953 -0.3473606 0.3645906 30.26192
## 4 4 0.8808056 0.7239800 0.1568256 -0.6044410 0.3489154 28.64438
## 5 5 0.8657836 0.6187252 0.2470584 -0.8688434 0.4041142 33.62679
## 6 6 0.9808469 0.6691661 0.3116808 -0.8023453 0.3254532 31.50847
## ÖVP FPÖ GRÜNE NEOS
## 1 30.84212 14.58013 12.12839 18.99799
## 2 13.63152 15.00865 17.36472 11.81114
## 3 17.34168 14.69525 15.32406 14.32886
## 4 18.75809 11.66338 18.18615 15.70096
## 5 12.99768 14.04125 17.32529 11.93927
## 6 15.15447 10.96311 20.05021 14.03864
head(aut_mig)
## bezirk nationality net_p ext_net_p int_net_p local_net_p
## 1 1 Austrian -0.024108004 -0.09811059 0.07400258 -0.36584982
## 2 2 Austrian -0.008464146 -0.07044653 0.06198239 -0.08092135
## 3 3 Austrian 0.004116163 -0.05942286 0.06353902 -0.21055977
## 4 4 Austrian 0.082479568 -0.02774039 0.11021995 -0.34475735
## 5 5 Austrian 0.036213530 -0.08294072 0.11915425 -0.52071695
## 6 6 Austrian 0.220244103 -0.01550328 0.23574738 -0.52707642
## local_internal_p SPÖ ÖVP FPÖ GRÜNE NEOS
## 1 0.1445852 19.11643 30.84212 14.58013 12.12839 18.99799
## 2 0.2713470 33.18164 13.63152 15.00865 17.36472 11.81114
## 3 0.2226034 30.26192 17.34168 14.69525 15.32406 14.32886
## 4 0.2044984 28.64438 18.75809 11.66338 18.18615 15.70096
## 5 0.2043232 33.62679 12.99768 14.04125 17.32529 11.93927
## 6 0.2037307 31.50847 15.15447 10.96311 20.05021 14.03864
head(for_mig)
## bezirk nationality net_p ext_net_p int_net_p local_net_p
## 1 1 Foreign 0.8226112 0.7808499 0.04176127 -0.3694749
## 2 2 Foreign 0.8978595 0.7071458 0.19071374 -0.3991587
## 3 3 Foreign 0.7623048 0.6888485 0.07345633 -0.1368008
## 4 4 Foreign 0.7983260 0.7517204 0.04660564 -0.2596837
## 5 5 Foreign 0.8295701 0.7016659 0.12790416 -0.3481265
## 6 6 Foreign 0.7606028 0.6846694 0.07593338 -0.2752688
## local_internal_p SPÖ ÖVP FPÖ GRÜNE NEOS
## 1 0.08950944 19.11643 30.84212 14.58013 12.12839 18.99799
## 2 0.20488554 33.18164 13.63152 15.00865 17.36472 11.81114
## 3 0.14198714 30.26192 17.34168 14.69525 15.32406 14.32886
## 4 0.14441701 28.64438 18.75809 11.66338 18.18615 15.70096
## 5 0.19979098 33.62679 12.99768 14.04125 17.32529 11.93927
## 6 0.12172245 31.50847 15.15447 10.96311 20.05021 14.03864
As an example for correlations, we use the perhaps most interesting example in our context, migration vs the right populist anti-migration party FPÖ.
cor(x = aut_mig$net_p,
y = aut_mig$FPÖ)
## [1] -0.8807193
mdl_1 = lm(data = aut_mig, FPÖ ~ net_p)
plot(x = aut_mig$net_p, y = aut_mig$FPÖ, ylab = "FPÖ election results (%)", xlab = "net migration of Austrian citizens (%)", main = "Net migration of Austrians vs FPÖ election results per bezirk") +
abline(a = mdl_1$coefficients[1], b = mdl_1$coefficients[2])
## integer(0)
cor(for_mig$net_p, for_mig$FPÖ)
## [1] -0.6007031
mdl_2 = lm(data = for_mig, FPÖ ~ net_p)
plot(x = for_mig$net_p, y = for_mig$FPÖ, ylab = "FPÖ election results (%)", xlab = "net migration of foreign citizens (%)", main = "Net migration of foreign citizens vs FPÖ election results per bezirk") +
abline(a = mdl_2$coefficients[1], b = mdl_2$coefficients[2])
## integer(0)
As can be seen, there is a very strong negative correlation between migration and FPÖ election results. Bezirks that have received relatively large amounts of migration, foreign and Austrian, tends to note vote for the FPÖ. Districts with relatively smaller amounts of migration instead have a strong tendency to vote for FPÖ. Viewing only the Austrian migration, the tendency is stronger (r = -0.88, p < 0.001); districts with a negative net migration of austrians strongly tend to vote FPÖ.
cors_tot = as.data.frame(tot_mig) %>% select(!c(bezirk))
cm = cor(cors_tot)
corrplot(cm, title = "total migration")
cors_aut = as.data.frame(aut_mig) %>% select(!c(bezirk, nationality))
cm = cor(cors_aut)
corrplot(cm, title = "Austrian citizens")
cors_for = as.data.frame(for_mig) %>% select(!c(bezirk, nationality))
cm = cor(cors_for)
corrplot(cm, title = "foreign citizens")
From the correlation plots, it can be seen that the migration variables are somewhat intercorrelated, and show correlation with FPÖ, GRÜNE and to some extent NEOS. ÖVP and SPÖ on the other hand show no correlation with different types, but they are inversely correlated with each other.
get_correlations = function(data, covar, parties) {
#takes a dataframe with data of party election results and a correlated variable, and returns a dataframe
#with the correlation coefficient between the correlated variable and the election result of each party
#with a 95% CI
r = c()
upper_ci = c()
lower_ci = c()
covar = data[, covar]
for (party in parties) {
part_results = data[, party]
correlation = cor.test(covar, part_results)
r = c(r, correlation$estimate)
upper_ci = c(upper_ci, correlation$conf.int[1])
lower_ci = c(lower_ci, correlation$conf.int[2])
}
df_out = data.frame(parties, r, upper_ci, lower_ci)
return(df_out)
}
To get a better look at the relationship, the correlation coefficients are calculated for the different variables and plotted as barcharts with 95% confident intervalls.
corrs_tot = get_correlations(tot_mig, "net_p", parliament)
ggplot(data = corrs_tot, aes (y = r, x = parties, fill = parties)) +
geom_bar(stat = 'identity', fill = c("red", "black", "blue", "green", "pink")) +
geom_errorbar(aes(ymin = lower_ci, ymax = upper_ci), width = 0.1) +
labs(title = "Corrrelation coefficients with 95% CI for voting patterns and migration (average yearly percent)", y = "Pearson's r")
corrs_aut = get_correlations(aut_mig, "net_p", parliament)
ggplot(data = corrs_aut, aes (y = r, x = parties, fill = parties)) +
geom_bar(stat = 'identity', fill = c("red", "black", "blue", "green", "pink")) +
geom_errorbar(aes(ymin = lower_ci, ymax = upper_ci), width = 0.1) +
labs(title = "Corrrelation coefficients with 95% CI for voting patterns and austrian migration to Vienna (average yearly percent)", y = "Pearson's r")
corrs_for = get_correlations(for_mig, "net_p", parliament)
ggplot(data = corrs_for, aes (y = r, x = parties, fill = parties)) +
geom_bar(stat = 'identity', fill = c("red", "black", "blue", "green", "pink")) +
geom_errorbar(aes(ymin = lower_ci, ymax = upper_ci), width = 0.1) +
labs(title = "Corrrelation coefficients with 95% CI for voting patterns and foreign migration (average yearly percent)", y = "Pearson's r")
As can be seen, districts with large amounts of migration relative to
their population tends to vote for the green and NEOS, and not vote for
FPÖ, for SPÖ and ÖVP no relation is found. The relationship is the
strongest for austrian migration.
corrs_aut = get_correlations(aut_mig, "local_net_p", parliament)
ggplot(data = corrs_aut, aes (y = r, x = parties, fill = parties)) +
geom_bar(stat = 'identity', fill = c("red", "black", "blue", "green", "pink")) +
geom_errorbar(aes(ymin = lower_ci, ymax = upper_ci), width = 0.1) +
labs(title = "Corrrelation coefficients with 95% CI 2024 election results and austrian migration within Vienna (average yearly percent)", y = "Pearson's r")
corrs_for = get_correlations(for_mig, "local_net_p", parliament)
ggplot(data = corrs_for, aes (y = r, x = parties, fill = parties)) +
geom_bar(stat = 'identity', fill = c("red", "black", "blue", "green", "pink")) +
geom_errorbar(aes(ymin = lower_ci, ymax = upper_ci), width = 0.1) +
labs(title = "Corrrelation coefficients with 95% CI for voting patterns and foreign migration (average yearly percent)", y = "Pearson's r")
corrs_tot = get_correlations(tot_mig, "local_net_p", parliament)
ggplot(data = corrs_tot, aes (y = r, x = parties, fill = parties)) +
geom_bar(stat = 'identity', fill = c("red", "black", "blue", "green", "pink")) +
geom_errorbar(aes(ymin = lower_ci, ymax = upper_ci), width = 0.1) +
labs(title = "Corrrelation coefficients with 95% CI for voting patterns and migration (average yearly net number of migrants", y = "Pearson's r")
Looking at the within Vienna migration, the relationship is the
opposite: districts with larger amount of migration tend to vote FPÖ and
not GRÜNE and NEOS and again, there seems to be no relationship between
migration and ÖVP and SPÖ.
As the independent variables are intercorrelated, it is necessary to build a larger model to view them together, and also to add sociodemographic factors to control for and isolate the impact of migration.
## add socioeconomic indicator
#average income 2021 https://www.data.gv.at/katalog/dataset/d76c0e8b-c599-4700-8a88-29d0d87e563d
income = income %>%
filter(REF_YEAR == 2021) %>%
select(DISTRICT_CODE, INC_TOT_VALUE)
colnames(income) = c("bezirk", "income")
income = income[2:23, ]
income$bezirk = as.factor(as.numeric(substr(income$bezirk, 2, 3)))
## add demographic indicators https://www.data.gv.at/katalog/dataset/9ecf5866-dbe8-4cb2-b156-5097c7eec01f
age = age %>% filter(REF_YEAR == 2024) %>% select(DISTRICT_CODE, AGE_AVE)
colnames(age) = c("bezirk", "age")
age = age[2:23, ]
age$age = age$age / 100
age$bezirk = as.factor(as.numeric(substr(age$bezirk, 2, 3)))
sex = sex %>% filter(REF_YEAR == 2024) %>% mutate(perc_fem = POP_FEM / POP_TOTAL * 100) %>% select(DISTRICT_CODE, perc_fem)
colnames(sex) = c("bezirk", "perc_fem")
sex$bezirk = as.factor(as.numeric(substr(sex$bezirk, 2, 3)))
aut_mig_var = as.data.frame(aut_mig) %>%
select(bezirk, net_p, local_net_p, local_internal_p) %>%
rename_with( ~ paste0(., "_aut"), c(net_p, local_net_p, local_internal_p))
for_mig_var = as.data.frame(for_mig) %>%
select(bezirk, net_p, local_net_p, local_internal_p) %>%
rename_with( ~ paste0(., "_for"), c(net_p, local_net_p, local_internal_p))
list_df = list(age, sex, income, aut_mig_var, for_mig_var, election[c(parliament, "bezirk")])
mdl_df = reduce(list_df, ~ inner_join(.x, .y, by = "bezirk"))
mdl_df = as.data.frame(mdl_df %>% select(!bezirk))
cm = cor(mdl_df)
corrplot(cm, method = "pie")
The demographic variables are intercorrelated, the migration variables
as well, with within district migration of foreigners being correlated
with demographic factors as well as ÖVP and SPÖ. ÖVP and SPÖ are
correlated with demographic factors while FPÖ, GRÜNE and NEOS are
correlated with migration variables.
df_mdl_FPÖ = subset(mdl_df, select = -c(SPÖ, ÖVP, GRÜNE, NEOS))
mdl_FPÖ = lm(data = df_mdl_FPÖ, FPÖ ~ .)
summary(mdl_FPÖ)
##
## Call:
## lm(formula = FPÖ ~ ., data = df_mdl_FPÖ)
##
## Residuals:
## Min 1Q Median 3Q Max
## -2.51660 -0.84668 0.04205 1.20960 1.89123
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 82.65137 49.18274 1.680 0.1187
## age -0.58091 0.75105 -0.773 0.4542
## perc_fem -1.06988 0.81114 -1.319 0.2118
## income 0.07364 0.41773 0.176 0.8630
## net_p_aut -29.93666 9.36994 -3.195 0.0077 **
## local_net_p_aut -3.29468 5.10949 -0.645 0.5312
## local_internal_p_aut 21.08788 24.18080 0.872 0.4003
## net_p_for 18.56827 7.04681 2.635 0.0218 *
## local_net_p_for 14.80330 5.39165 2.746 0.0177 *
## local_internal_p_for -38.84309 30.68082 -1.266 0.2295
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.843 on 12 degrees of freedom
## Multiple R-squared: 0.9533, Adjusted R-squared: 0.9184
## F-statistic: 27.25 on 9 and 12 DF, p-value: 1.21e-06
rsq(mdl_FPÖ)
## [1] 0.9533451
AIC(mdl_FPÖ)
## [1] 97.98824
RMSE(mdl_FPÖ)
## [1] 1.360792
vif(mdl_FPÖ)
## age perc_fem income
## 11.856388 6.238994 16.930084
## net_p_aut local_net_p_aut local_internal_p_aut
## 13.638026 15.032455 9.393337
## net_p_for local_net_p_for local_internal_p_for
## 14.428220 17.739515 10.131135
res = resid(mdl_FPÖ)
qqnorm(res)
qqline(res) # normally distributed residuals
mdl_FPÖ_scale = lm(data = as.data.frame(apply(
df_mdl_FPÖ, MARGIN = 2, FUN = scale
)), FPÖ ~ .)
summary(mdl_FPÖ_scale)
##
## Call:
## lm(formula = FPÖ ~ ., data = as.data.frame(apply(df_mdl_FPÖ,
## MARGIN = 2, FUN = scale)))
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.39027 -0.13130 0.00652 0.18759 0.29329
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -5.838e-16 6.092e-02 0.000 1.0000
## age -1.661e-01 2.147e-01 -0.773 0.4542
## perc_fem -2.054e-01 1.557e-01 -1.319 0.2118
## income 4.523e-02 2.566e-01 0.176 0.8630
## net_p_aut -7.357e-01 2.303e-01 -3.195 0.0077 **
## local_net_p_aut -1.559e-01 2.418e-01 -0.645 0.5312
## local_internal_p_aut 1.667e-01 1.911e-01 0.872 0.4003
## net_p_for 6.241e-01 2.368e-01 2.635 0.0218 *
## local_net_p_for 7.211e-01 2.626e-01 2.746 0.0177 *
## local_internal_p_for -2.513e-01 1.985e-01 -1.266 0.2295
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2857 on 12 degrees of freedom
## Multiple R-squared: 0.9533, Adjusted R-squared: 0.9184
## F-statistic: 27.25 on 9 and 12 DF, p-value: 1.21e-06
df_mdl_FPÖ_2 = subset(df_mdl_FPÖ,
select = -c(local_net_p_aut, age, local_internal_p_for))
mdl_FPÖ_2 = lm(data = df_mdl_FPÖ_2, FPÖ ~ .)
summary(mdl_FPÖ_2)
##
## Call:
## lm(formula = FPÖ ~ ., data = df_mdl_FPÖ_2)
##
## Residuals:
## Min 1Q Median 3Q Max
## -2.662 -1.271 0.254 1.164 2.570
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 35.94278 32.45430 1.107 0.28553
## perc_fem -0.56284 0.71629 -0.786 0.44424
## income -0.07536 0.20014 -0.377 0.71178
## net_p_aut -19.58364 5.02947 -3.894 0.00144 **
## local_internal_p_aut 3.89830 13.43561 0.290 0.77568
## net_p_for 19.12883 5.06913 3.774 0.00184 **
## local_net_p_for 20.21386 3.77834 5.350 8.1e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.823 on 15 degrees of freedom
## Multiple R-squared: 0.9429, Adjusted R-squared: 0.9201
## F-statistic: 41.3 on 6 and 15 DF, p-value: 1.716e-08
rsq(mdl_FPÖ_2)
## [1] 0.942925
RMSE(mdl_FPÖ_2)
## [1] 1.505101
AIC(mdl_FPÖ_2)
## [1] 96.42314
vif(mdl_FPÖ_2)
## perc_fem income net_p_aut
## 4.971202 3.970886 4.014986
## local_internal_p_aut net_p_for local_net_p_for
## 2.963162 7.628822 8.901488
res = resid(mdl_FPÖ_2)
qqnorm(res)
qqline(res)
shapiro.test(res) #no significant support for deviation from normality
##
## Shapiro-Wilk normality test
##
## data: res
## W = 0.96679, p-value = 0.6369
mdl_FPÖ_2_scale = lm(data = as.data.frame(apply(
df_mdl_FPÖ_2, MARGIN = 2, FUN = scale
)), FPÖ ~ .)
summary(mdl_FPÖ_2_scale)
##
## Call:
## lm(formula = FPÖ ~ ., data = as.data.frame(apply(df_mdl_FPÖ_2,
## MARGIN = 2, FUN = scale)))
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.41282 -0.19704 0.03938 0.18054 0.39848
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -1.907e-16 6.027e-02 0.000 1.00000
## perc_fem -1.081e-01 1.375e-01 -0.786 0.44424
## income -4.629e-02 1.229e-01 -0.377 0.71178
## net_p_aut -4.813e-01 1.236e-01 -3.894 0.00144 **
## local_internal_p_aut 3.081e-02 1.062e-01 0.290 0.77568
## net_p_for 6.429e-01 1.704e-01 3.774 0.00184 **
## local_net_p_for 9.846e-01 1.840e-01 5.350 8.1e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2827 on 15 degrees of freedom
## Multiple R-squared: 0.9429, Adjusted R-squared: 0.9201
## F-statistic: 41.3 on 6 and 15 DF, p-value: 1.716e-08
plot_coefs(mdl_FPÖ_2_scale, colors = "darkblue") + theme_apa() + ggtitle("Beta coefficients for linear regression model, FPÖ results as dependent variable")
## Registered S3 methods overwritten by 'broom':
## method from
## tidy.glht jtools
## tidy.summary.glht jtools
## Loading required namespace: broom.mixed
best_mdl_FPÖ = mdl_FPÖ_2
When accounting for other factors such as income and gender, migration remains significant predictors of FPÖ election results. Specifically, an increase in Austrian migration is associated with worse results for FPÖ, and an increase in foreign migration both within and from outside Vienna is associated with increased support for FPÖ. The most important variable for FPÖ support is given by looking at which district most foreign citizens already living in Vienna are moving to.
df_mdl_GRN = subset(mdl_df, select = -c(SPÖ, ÖVP, FPÖ, NEOS))
mdl_GRN = lm(data = df_mdl_GRN, GRÜNE ~ .)
summary(mdl_GRN)
##
## Call:
## lm(formula = GRÜNE ~ ., data = df_mdl_GRN)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.5573 -0.8215 -0.3359 0.8486 2.1401
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 52.069941 36.128475 1.441 0.175098
## age -0.492724 0.551702 -0.893 0.389364
## perc_fem 0.002438 0.595847 0.004 0.996802
## income -0.023943 0.306851 -0.078 0.939091
## net_p_aut 23.207380 6.882937 3.372 0.005552 **
## local_net_p_aut 2.672406 3.753313 0.712 0.490069
## local_internal_p_aut -9.556891 17.762641 -0.538 0.600391
## net_p_for -24.237004 5.176418 -4.682 0.000530 ***
## local_net_p_for -18.686354 3.960580 -4.718 0.000499 ***
## local_internal_p_for 9.763975 22.537400 0.433 0.672530
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.353 on 12 degrees of freedom
## Multiple R-squared: 0.9526, Adjusted R-squared: 0.9171
## F-statistic: 26.82 on 9 and 12 DF, p-value: 1.321e-06
rsq(mdl_GRN)
## [1] 0.9526432
AIC(mdl_GRN)
## [1] 84.41593
RMSE(mdl_GRN)
## [1] 0.9996054
vif(mdl_GRN)
## age perc_fem income
## 11.856388 6.238994 16.930084
## net_p_aut local_net_p_aut local_internal_p_aut
## 13.638026 15.032455 9.393337
## net_p_for local_net_p_for local_internal_p_for
## 14.428220 17.739515 10.131135
res = resid(mdl_GRN)
qqnorm(res)
qqline(res)
shapiro.test(res) # no support for deviation from normality
##
## Shapiro-Wilk normality test
##
## data: res
## W = 0.93514, p-value = 0.157
df_mdl_GRN_2 = subset(
df_mdl_GRN,
select = -c(
local_internal_p_aut,
local_internal_p_for,
local_net_p_aut,
age
)
)
mdl_GRN_2 = lm(data = df_mdl_GRN_2, GRÜNE ~ .)
summary(mdl_GRN_2)
##
## Call:
## lm(formula = GRÜNE ~ ., data = df_mdl_GRN_2)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.0935 -0.9878 -0.3694 0.8841 2.0470
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 24.4123 20.2417 1.206 0.2453
## perc_fem 0.2138 0.4172 0.513 0.6153
## income -0.2943 0.1203 -2.446 0.0264 *
## net_p_aut 21.5071 3.3563 6.408 8.67e-06 ***
## net_p_for -20.6735 3.4508 -5.991 1.88e-05 ***
## local_net_p_for -16.3672 2.5502 -6.418 8.51e-06 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.245 on 16 degrees of freedom
## Multiple R-squared: 0.9465, Adjusted R-squared: 0.9298
## F-statistic: 56.66 on 5 and 16 DF, p-value: 1.311e-09
rsq(mdl_GRN_2)
## [1] 0.9465394
AIC(mdl_GRN_2)
## [1] 79.0831
RMSE(mdl_GRN)
## [1] 0.9996054
vif(mdl_GRN_2)
## perc_fem income net_p_aut net_p_for local_net_p_for
## 3.612733 3.075519 3.830217 7.573179 8.687109
res = resid(mdl_GRN_2)
qqnorm(res)
qqline(res)
shapiro.test(res)
##
## Shapiro-Wilk normality test
##
## data: res
## W = 0.85651, p-value = 0.004435
mdl_GRN_2_scale = lm(data = as.data.frame(scale(df_mdl_GRN_2)), GRÜNE ~ .)
summary(mdl_GRN_2_scale)
##
## Call:
## lm(formula = GRÜNE ~ ., data = as.data.frame(scale(df_mdl_GRN_2)))
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.23259 -0.21009 -0.07856 0.18805 0.43539
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -7.546e-16 5.647e-02 0.000 1.0000
## perc_fem 5.632e-02 1.099e-01 0.513 0.6153
## income -2.479e-01 1.014e-01 -2.446 0.0264 *
## net_p_aut 7.249e-01 1.131e-01 6.408 8.67e-06 ***
## net_p_for -9.530e-01 1.591e-01 -5.991 1.88e-05 ***
## local_net_p_for -1.093e+00 1.704e-01 -6.418 8.51e-06 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2649 on 16 degrees of freedom
## Multiple R-squared: 0.9465, Adjusted R-squared: 0.9298
## F-statistic: 56.66 on 5 and 16 DF, p-value: 1.311e-09
plot_coefs(mdl_GRN_2_scale, colors = "green") + theme_apa() + ggtitle("Beta coefficients for linear regression model, GRÜNE results as dependent variable")
## Loading required namespace: broom.mixed
best_mdl_GRN = mdl_GRN_2
The model of GRÜNE results is a somewhat better fit than the one for FPÖ, reveealing an inverse of the relationship between migration and election results as observed for FPÖ.
A difference observed is that income has a significant effect on the support for GRÜNE, where higher income is associated with lower support. The effect is however small.
df_mdl_ÖVP = subset(mdl_df, select = -c(SPÖ, GRÜNE, FPÖ, NEOS))
mdl_ÖVP = lm(data = df_mdl_ÖVP, ÖVP ~ .)
summary(mdl_ÖVP)
##
## Call:
## lm(formula = ÖVP ~ ., data = df_mdl_ÖVP)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.47473 -0.51261 -0.04775 0.36151 1.98023
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -149.8813 28.5492 -5.250 0.000204 ***
## age 2.0827 0.4360 4.777 0.000451 ***
## perc_fem 1.2903 0.4708 2.740 0.017920 *
## income 0.1518 0.2425 0.626 0.543026
## net_p_aut -11.5059 5.4390 -2.115 0.055986 .
## local_net_p_aut -0.6927 2.9659 -0.234 0.819271
## local_internal_p_aut 7.0329 14.0363 0.501 0.625397
## net_p_for 11.7382 4.0905 2.870 0.014099 *
## local_net_p_for 5.8481 3.1297 1.869 0.086278 .
## local_internal_p_for 6.8678 17.8094 0.386 0.706519
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.07 on 12 degrees of freedom
## Multiple R-squared: 0.9714, Adjusted R-squared: 0.9499
## F-statistic: 45.24 on 9 and 12 DF, p-value: 6.829e-08
rsq(mdl_ÖVP)
## [1] 0.9713723
AIC(mdl_ÖVP)
## [1] 74.05605
RMSE(mdl_ÖVP)
## [1] 0.789902
vif(mdl_ÖVP)
## age perc_fem income
## 11.856388 6.238994 16.930084
## net_p_aut local_net_p_aut local_internal_p_aut
## 13.638026 15.032455 9.393337
## net_p_for local_net_p_for local_internal_p_for
## 14.428220 17.739515 10.131135
res = resid(mdl_ÖVP)
qqnorm(res)
qqline(res)
shapiro.test(res) # no support for deviation from normality
##
## Shapiro-Wilk normality test
##
## data: res
## W = 0.96156, p-value = 0.5214
df_mdl_ÖVP_2 = subset(
df_mdl_ÖVP,
select = -c(
income,
local_internal_p_aut,
local_internal_p_aut,
local_internal_p_for,
local_net_p_aut
)
)
mdl_ÖVP_2 = lm(data = df_mdl_ÖVP_2, ÖVP ~ .)
summary(mdl_ÖVP_2)
##
## Call:
## lm(formula = ÖVP ~ ., data = df_mdl_ÖVP_2)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.83169 -0.49195 -0.01817 0.30818 2.16547
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -159.2839 12.8132 -12.431 1.23e-09 ***
## age 2.1517 0.1667 12.908 7.10e-10 ***
## perc_fem 1.5433 0.2632 5.863 2.40e-05 ***
## net_p_aut -12.2462 2.7430 -4.464 0.000391 ***
## net_p_for 12.0547 2.9213 4.127 0.000791 ***
## local_net_p_for 5.4384 2.2411 2.427 0.027420 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.006 on 16 degrees of freedom
## Multiple R-squared: 0.9662, Adjusted R-squared: 0.9557
## F-statistic: 91.53 on 5 and 16 DF, p-value: 3.424e-11
rsq(mdl_ÖVP_2)
## [1] 0.966221
AIC(mdl_ÖVP_2)
## [1] 69.69633
RMSE(mdl_ÖVP_2)
## [1] 0.8580328
vif(mdl_ÖVP_2)
## age perc_fem net_p_aut net_p_for local_net_p_for
## 1.958857 2.203405 3.919690 8.315345 10.278273
res = resid(mdl_ÖVP_2)
qqnorm(res)
qqline(res)
shapiro.test(res) # no support for deviation from normality
##
## Shapiro-Wilk normality test
##
## data: res
## W = 0.94294, p-value = 0.2275
mdl_ÖVP_2_scale = lm(data = as.data.frame(scale(df_mdl_ÖVP_2)), ÖVP ~ .)
summary(mdl_ÖVP_2_scale)
##
## Call:
## lm(formula = ÖVP ~ ., data = as.data.frame(scale(df_mdl_ÖVP_2)))
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.38333 -0.10295 -0.00380 0.06449 0.45318
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2.758e-15 4.489e-02 0.000 1.000000
## age 8.301e-01 6.431e-02 12.908 7.1e-10 ***
## perc_fem 3.999e-01 6.820e-02 5.863 2.4e-05 ***
## net_p_aut -4.061e-01 9.097e-02 -4.464 0.000391 ***
## net_p_for 5.468e-01 1.325e-01 4.127 0.000791 ***
## local_net_p_for 3.575e-01 1.473e-01 2.427 0.027420 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2106 on 16 degrees of freedom
## Multiple R-squared: 0.9662, Adjusted R-squared: 0.9557
## F-statistic: 91.53 on 5 and 16 DF, p-value: 3.424e-11
plot_coefs(mdl_ÖVP_2_scale, colors = "black") + theme_apa() + ggtitle("Beta coefficients for linear regression model, ÖVP results as dependent variable")
## Loading required namespace: broom.mixed
best_mdl_ÖVP = mdl_ÖVP_2
The model of ÖVP has a good fit and is similar to that of FPÖ with regard to migration: Increased Austrian migration is associated with lower support for ÖVP, increased foreign migration is associated with increased support for ÖVP, the size of the effect is however smaller. In the case of ÖVP, age and gender are significant variables, where the older the average population and the larger the percent of female residents, the more support for ÖVP.
df_mdl_SPÖ = subset(mdl_df, select = -c(ÖVP, GRÜNE, FPÖ, NEOS))
mdl_SPÖ = lm(data = df_mdl_SPÖ, SPÖ ~ .)
summary(mdl_SPÖ)
##
## Call:
## lm(formula = SPÖ ~ ., data = df_mdl_SPÖ)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.7030 -0.6481 0.1453 0.7128 1.7223
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 129.9974 33.5063 3.880 0.00219 **
## age -0.9037 0.5117 -1.766 0.10275
## perc_fem -0.9418 0.5526 -1.704 0.11406
## income -0.3828 0.2846 -1.345 0.20347
## net_p_aut 14.1291 6.3834 2.213 0.04699 *
## local_net_p_aut 3.3796 3.4809 0.971 0.35076
## local_internal_p_aut -19.7894 16.4734 -1.201 0.25281
## net_p_for -1.0783 4.8007 -0.225 0.82606
## local_net_p_for 3.5115 3.6731 0.956 0.35794
## local_internal_p_for 16.4002 20.9016 0.785 0.44788
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.255 on 12 degrees of freedom
## Multiple R-squared: 0.9505, Adjusted R-squared: 0.9134
## F-statistic: 25.6 on 9 and 12 DF, p-value: 1.713e-06
rsq(mdl_SPÖ)
## [1] 0.9504881
AIC(mdl_SPÖ)
## [1] 81.10057
RMSE(mdl_SPÖ)
## [1] 0.9270537
vif(mdl_SPÖ)
## age perc_fem income
## 11.856388 6.238994 16.930084
## net_p_aut local_net_p_aut local_internal_p_aut
## 13.638026 15.032455 9.393337
## net_p_for local_net_p_for local_internal_p_for
## 14.428220 17.739515 10.131135
res = resid(mdl_SPÖ)
qqnorm(res)
qqline(res)
shapiro.test(res) # no support for deviation from normality
##
## Shapiro-Wilk normality test
##
## data: res
## W = 0.96812, p-value = 0.6675
df_mdl_SPÖ_2 = subset(df_mdl_SPÖ,
select = -c(income, local_net_p_for, local_internal_p_for))
mdl_SPÖ_2 = lm(data = df_mdl_SPÖ_2, SPÖ ~ .)
summary(mdl_SPÖ_2)
##
## Call:
## lm(formula = SPÖ ~ ., data = df_mdl_SPÖ_2)
##
## Residuals:
## Min 1Q Median 3Q Max
## -2.35145 -0.63241 0.00802 0.88703 1.54004
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 181.6354 17.1004 10.622 2.25e-08 ***
## age -1.5765 0.2563 -6.151 1.85e-05 ***
## perc_fem -1.5838 0.4434 -3.572 0.00278 **
## net_p_aut 7.3028 4.7264 1.545 0.14315
## local_net_p_aut 0.9959 2.8053 0.355 0.72752
## local_internal_p_aut -11.3156 10.4516 -1.083 0.29606
## net_p_for -3.1982 2.6648 -1.200 0.24869
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.283 on 15 degrees of freedom
## Multiple R-squared: 0.9353, Adjusted R-squared: 0.9095
## F-statistic: 36.16 on 6 and 15 DF, p-value: 4.316e-08
rsq(mdl_SPÖ_2)
## [1] 0.9353367
AIC(mdl_SPÖ_2)
## [1] 80.97418
RMSE(mdl_SPÖ_2)
## [1] 1.059447
vif(mdl_SPÖ_2) # no VIF > 10
## age perc_fem net_p_aut
## 2.847055 3.843767 7.155921
## local_net_p_aut local_internal_p_aut net_p_for
## 9.344554 3.618923 4.254970
res = resid(mdl_SPÖ_2)
qqnorm(res)
qqline(res)
shapiro.test(res) # no support for deviation from normality
##
## Shapiro-Wilk normality test
##
## data: res
## W = 0.9623, p-value = 0.5371
mdl_SPÖ_2_scale = lm(data = as.data.frame(scale(df_mdl_SPÖ_2)), SPÖ ~ .)
summary(mdl_SPÖ_2_scale)
##
## Call:
## lm(formula = SPÖ ~ ., data = as.data.frame(scale(df_mdl_SPÖ_2)))
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.55142 -0.14830 0.00188 0.20801 0.36114
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -2.162e-15 6.415e-02 0.000 1.00000
## age -6.815e-01 1.108e-01 -6.151 1.85e-05 ***
## perc_fem -4.598e-01 1.287e-01 -3.572 0.00278 **
## net_p_aut 2.714e-01 1.756e-01 1.545 0.14315
## local_net_p_aut 7.125e-02 2.007e-01 0.355 0.72752
## local_internal_p_aut -1.352e-01 1.249e-01 -1.083 0.29606
## net_p_for -1.625e-01 1.354e-01 -1.200 0.24869
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.3009 on 15 degrees of freedom
## Multiple R-squared: 0.9353, Adjusted R-squared: 0.9095
## F-statistic: 36.16 on 6 and 15 DF, p-value: 4.316e-08
plot_coefs(mdl_SPÖ_2_scale, colors = "red") + theme_apa() + ggtitle("Beta coefficients for linear regression model, SPÖ results as dependent variable")
## Loading required namespace: broom.mixed
best_mdl_SPÖ = mdl_SPÖ_2
For SPÖ, no significant association between migration and party support is found when accounting for other demographic indicators. Instead, an increase in age and percentage of females is associated with lower support for SPÖ.
df_mdl_NEOS = subset(mdl_df, select = -c(ÖVP, GRÜNE, FPÖ, SPÖ))
mdl_NEOS = lm(data = df_mdl_NEOS, NEOS ~ .)
summary(mdl_NEOS)
##
## Call:
## lm(formula = NEOS ~ ., data = df_mdl_NEOS)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.14283 -0.57189 -0.06258 0.60264 1.25028
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -54.8978 26.1362 -2.100 0.0575 .
## age 0.1946 0.3991 0.488 0.6347
## perc_fem 0.9475 0.4311 2.198 0.0483 *
## income 0.4005 0.2220 1.804 0.0963 .
## net_p_aut 3.7944 4.9793 0.762 0.4608
## local_net_p_aut -1.9303 2.7152 -0.711 0.4907
## local_internal_p_aut 5.7904 12.8499 0.451 0.6603
## net_p_for -2.1540 3.7447 -0.575 0.5758
## local_net_p_for -3.9712 2.8652 -1.386 0.1910
## local_internal_p_for -0.5123 16.3041 -0.031 0.9754
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.9791 on 12 degrees of freedom
## Multiple R-squared: 0.9605, Adjusted R-squared: 0.9309
## F-statistic: 32.45 on 9 and 12 DF, p-value: 4.537e-07
rsq(mdl_NEOS) #0.96
## [1] 0.9605293
AIC(mdl_NEOS) #70
## [1] 70.17048
RMSE(mdl_NEOS) #0.72
## [1] 0.7231382
vif(mdl_NEOS) #
## age perc_fem income
## 11.856388 6.238994 16.930084
## net_p_aut local_net_p_aut local_internal_p_aut
## 13.638026 15.032455 9.393337
## net_p_for local_net_p_for local_internal_p_for
## 14.428220 17.739515 10.131135
res = resid(mdl_NEOS)
qqnorm(res)
qqline(res)
shapiro.test(res) # no support for deviation from normality
##
## Shapiro-Wilk normality test
##
## data: res
## W = 0.95895, p-value = 0.4684
df_mdl_NEOS_2 = subset(
df_mdl_NEOS,
select = -c(
age,
local_net_p_aut,
local_internal_p_aut,
local_internal_p_for
)
)
mdl_NEOS_2 = lm(data = df_mdl_NEOS_2, NEOS ~ .)
summary(mdl_NEOS_2)
##
## Call:
## lm(formula = NEOS ~ ., data = df_mdl_NEOS_2)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.93756 -0.71938 -0.06721 0.72112 1.22473
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -41.49547 14.43990 -2.874 0.01103 *
## perc_fem 0.85017 0.29763 2.857 0.01143 *
## income 0.47958 0.08585 5.586 4.09e-05 ***
## net_p_aut 4.59651 2.39433 1.920 0.07290 .
## net_p_for -3.59858 2.46171 -1.462 0.16315
## local_net_p_for -5.55879 1.81928 -3.055 0.00755 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.8884 on 16 degrees of freedom
## Multiple R-squared: 0.9567, Adjusted R-squared: 0.9431
## F-statistic: 70.65 on 5 and 16 DF, p-value: 2.476e-10
rsq(mdl_NEOS_2)
## [1] 0.9566712
AIC(mdl_NEOS_2)
## [1] 64.22221
RMSE(mdl_NEOS_2)
## [1] 0.7576569
vif(mdl_NEOS_2)
## perc_fem income net_p_aut net_p_for local_net_p_for
## 3.612733 3.075519 3.830217 7.573179 8.687109
res = resid(mdl_NEOS_2)
qqnorm(res)
qqline(res)
shapiro.test(res) # no support for deviation from normality
##
## Shapiro-Wilk normality test
##
## data: res
## W = 0.89561, p-value = 0.02431
mdl_NEOS_2_scale = lm(data = as.data.frame(scale(df_mdl_NEOS_2)), NEOS ~ .)
summary(mdl_NEOS_2_scale)
##
## Call:
## lm(formula = NEOS ~ ., data = as.data.frame(scale(df_mdl_NEOS_2)))
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.25166 -0.19309 -0.01804 0.19356 0.32874
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 7.507e-16 5.084e-02 0.000 1.00000
## perc_fem 2.825e-01 9.891e-02 2.857 0.01143 *
## income 5.098e-01 9.126e-02 5.586 4.09e-05 ***
## net_p_aut 1.955e-01 1.018e-01 1.920 0.07290 .
## net_p_for -2.093e-01 1.432e-01 -1.462 0.16315
## local_net_p_for -4.686e-01 1.534e-01 -3.055 0.00755 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2385 on 16 degrees of freedom
## Multiple R-squared: 0.9567, Adjusted R-squared: 0.9431
## F-statistic: 70.65 on 5 and 16 DF, p-value: 2.476e-10
plot_coefs(mdl_NEOS_2_scale, colors = "pink") + theme_apa() + ggtitle("Beta coefficients for linear regression model, NEOS results as dependent variable")
## Loading required namespace: broom.mixed
best_mdl_NEOS = mdl_NEOS_2
In the case of the NEOS, neither external nor internal migration has a significant effect, instead, gender and income and gender have the largest effect. An increase in percentage of female residents and increase in average income is associated with increased support for NEOS. An increase in foreign immigrants within Vienna is also associated with lower support for NEOS.
##Summary
plot_summs(
best_mdl_FPÖ,
best_mdl_GRN,
best_mdl_ÖVP,
best_mdl_SPÖ,
best_mdl_NEOS,
colors = c("blue", 'green', 'black', 'red', 'pink'),
model.names = c("FPÖ", 'GRÜNE', 'ÖVP', "SPÖ", 'NEOS')
) +
theme_apa() + ggtitle("Comparison of beta coefficients for modelling party support in Vienna")
## Loading required namespace: broom.mixed
## Loading required namespace: broom.mixed
## Loading required namespace: broom.mixed
## Loading required namespace: broom.mixed
## Loading required namespace: broom.mixed
metrics = data.frame(
Model = c("FPÖ", "GRÜNE", "ÖVP", "SPÖ", "NEOS"),
R2 = round(c(summary(best_mdl_FPÖ)$r.squared,
summary(best_mdl_GRN)$r.squared,
summary(best_mdl_ÖVP)$r.squared,
summary(best_mdl_SPÖ)$r.squared,
summary(best_mdl_NEOS)$r.squared),3),
AIC = round(c(AIC(best_mdl_FPÖ),
AIC(best_mdl_GRN),
AIC(best_mdl_ÖVP),
AIC(best_mdl_SPÖ),
AIC(best_mdl_NEOS)),3),
RMSE = round(c(RMSE(best_mdl_FPÖ),
RMSE(best_mdl_GRN),
RMSE(best_mdl_ÖVP),
RMSE(best_mdl_SPÖ),
RMSE(best_mdl_NEOS)),3)
)
gt(metrics)
| Model | R2 | AIC | RMSE |
|---|---|---|---|
| FPÖ | 0.943 | 96.423 | 1.505 |
| GRÜNE | 0.947 | 79.083 | 1.062 |
| ÖVP | 0.966 | 69.696 | 0.858 |
| SPÖ | 0.935 | 80.974 | 1.059 |
| NEOS | 0.957 | 64.222 | 0.758 |
All in all, several models could successfully be created explaining most of the variability in election results between the Viennese districts. The best model was the one of ÖVP, with an R2 of 0.966 and a Root mean squared error of 0.180. Across the board, recent migration is a significant predictor of election results for all parties except for SPÖ, whose results are explained by a combination of age and gender.
A more positive net migration of persons with foreign citizen ship is associated with a better result for ÖVP and FPÖ, while a more positive net migration of Austrians is associated with a worse result for ÖVP and FPÖ. In the case of ÖVP, the sociodemographic factors age and gender are significant predictors as well. In the case of ÖVP, age is the most important variable, but for FPÖ it is within Vienna migration of foreigners.
The modell of the greens results tell a smiliar story, but their results are positively associated with net migration of austrians and negatively associated with migration of foreigners. Interestingly, income is here significant predictor with a negative direction, indicating that the greens enjoy support from voters with lower income, which could be due to e.g. Students preferring this party.
Just like for the greens, the NEOS has income as a significant predictor, being associated with a larger income however. Here, the effect of income is even the largest and most important, while migration does not have a perticular impact.